package com.fred.xlog

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.util.concurrent.LinkedBlockingDeque


object Log {

    const val LEVEL_VERBOSE = 0
    const val LEVEL_DEBUG = 1
    const val LEVEL_INFO = 2
    const val LEVEL_WARNING = 3
    const val LEVEL_ERROR = 4
    const val LEVEL_FATAL = 5
    const val LEVEL_NONE = 6

    // defaults to LEVEL_NONE
    private var level = LEVEL_NONE
    private val cacheLog by lazy {
        LinkedBlockingDeque<NotInitInfo>()
    }

    private var impl: LogImp? = null

    fun setLogImp(imp: LogImp?) {
        impl = imp
        Thread {
            while (true) {
                val cache = cacheLog.poll()
                if (cache != null) {
                    when (cache.flag) {
                        0 -> f(cache.tag, cache.log)
                        1 -> e(cache.tag, cache.log)
                        2 -> w(cache.tag, cache.log)
                        3 -> i(cache.tag, cache.log)
                        4 -> d(cache.tag, cache.log)
                        5 -> v(cache.tag, cache.log)
                    }
                } else {
                    break
                }
            }
        }.start()
    }

    fun appenderOpen(level: Int, mode: Int, cacheDir: String?, logDir: String?, namePrefix: String?, cacheDays: Int) {
        impl?.appenderOpen(level, mode, cacheDir, logDir, namePrefix, cacheDays)
    }

    fun appenderFlushSync(isSync: Boolean) {
         impl?.appenderFlush(0, isSync)
    }

    fun setMaxFileSize(maxFileSize:Long) {
        impl?.setMaxFileSize(0, maxFileSize)
    }

    val logLevel: Int
        get() = impl?.getLogLevel(0)?:LEVEL_NONE

    fun setLevel(level: Int) {
        Log.level = level
    }

    fun setConsoleLogOpen(isOpen: Boolean) {
        impl?.setConsoleLogOpen(0, isOpen)
    }

    fun f(log: String?) {
        f("",log)
    }

    fun f(tag: String?, log: String?) {
        val logImpl = impl
        if (logImpl != null) {
            if (logImpl.getLogLevel(0) <= LEVEL_FATAL) {
                logImpl.logF(0, tag?:"", "", "", 0, 0, Thread.currentThread().id, 0, log?:"")
            }
        } else {
            cacheLog.offer(NotInitInfo(0,tag, log))
        }
    }

    fun e(log: String?) {
        e("",log)
    }

    fun e(tag: String?, log: String?) {
        val logImpl = impl
        if (logImpl != null) {
            if (logImpl.getLogLevel(0) <= LEVEL_ERROR) {
                logImpl.logE(0, tag?:"", "", "", 0, 0, Thread.currentThread().id, 0, log?:"")
            }
        } else {
            cacheLog.offer(NotInitInfo(1,tag, log))
        }
    }

    fun w(log: String?) {
        w("",log)
    }

    fun w(tag: String?, log: String?) {
        val logImpl = impl
        if (logImpl != null) {
            if (logImpl.getLogLevel(0) <= LEVEL_WARNING) {
                logImpl.logW(0, tag?:"", "", "", 0, 0, Thread.currentThread().id, 0, log?:"")
            }
        } else {
            cacheLog.offer(NotInitInfo(2,tag, log))
        }
    }

    fun i(log: String?) {
        i("",log)
    }

    fun i(tag: String?, log: String?) {
        val logImpl = impl
        if (logImpl != null) {
            if (logImpl.getLogLevel(0) <= LEVEL_INFO) {
                logImpl.logI(0, tag?:"", "", "", 0, 0, Thread.currentThread().id, 0, log?:"")
            }
        } else {
            cacheLog.offer(NotInitInfo(3,tag, log))
        }
    }

    fun d(log: String?) {
        d("",log)
    }

    fun d(tag: String?, log: String?) {
        val logImpl = impl
        if (logImpl != null) {
            if (logImpl.getLogLevel(0) <= LEVEL_DEBUG) {
                logImpl.logD(0, tag?:"", "", "", 0, 0, Thread.currentThread().id, 0, log?:"")
            }
        } else {
            cacheLog.offer(NotInitInfo(4,tag, log))
        }
    }

    fun v(log: String?) {
        v("",log)
    }

    fun v(tag: String?, log: String?) {
        val logImpl = impl
        if (logImpl != null) {
            if (logImpl.getLogLevel(0) <= LEVEL_VERBOSE) {
                logImpl.logV(0, tag?:"", "", "", 0, 0, Thread.currentThread().id, 0, log?:"")
            }
        } else {
            cacheLog.offer(NotInitInfo(5,tag, log))
        }
    }

    interface LogImp {
        fun logV(logInstancePtr: Long, tag: String?, fileName: String?, funcName: String?,
            line: Int, pid: Int, tid: Long, mainTid: Long, log: String?)

        fun logI(logInstancePtr: Long, tag: String?, fileName: String?, funcName: String?,
            line: Int, pid: Int, tid: Long, mainTid: Long, log: String?)

        fun logD(logInstancePtr: Long, tag: String?, fileName: String?, funcName: String?,
            line: Int, pid: Int, tid: Long, mainTid: Long, log: String?)

        fun logW(logInstancePtr: Long, tag: String?, fileName: String?, funcName: String?,
            line: Int, pid: Int, tid: Long, mainTid: Long, log: String?)

        fun logE(logInstancePtr: Long, tag: String?, fileName: String?, funcName: String?,
            line: Int, pid: Int, tid: Long, mainTid: Long, log: String?)

        fun logF(logInstancePtr: Long, tag: String?, fileName: String?, funcName: String?,
            line: Int, pid: Int, tid: Long, mainTid: Long, log: String?)

        fun getLogLevel(logInstancePtr: Long): Int
        fun setAppenderMode(logInstancePtr: Long, mode: Int)
        fun openLogInstance(level: Int, mode: Int, cacheDir: String?, logDir: String?,
                            namePrefix: String?, cacheDays: Int): Long

        fun getXlogInstance(namePrefix: String?): Long
        fun releaseXlogInstance(namePrefix: String?)
        fun appenderOpen(level: Int, mode: Int, cacheDir: String?, logDir: String?, namePrefix: String?, cacheDays: Int)
        fun appenderClose()
        fun appenderFlush(logInstancePtr: Long, isSync: Boolean)
        fun setConsoleLogOpen(logInstancePtr: Long, isOpen: Boolean)
        fun setMaxFileSize(logInstancePtr: Long, aliveSeconds: Long)
        fun setMaxAliveTime(logInstancePtr: Long, aliveSeconds: Long)
    }

    private data class NotInitInfo(
        val flag:Int,
        val tag: String? = null,
        val log: String? = null
    )


}

